﻿#ifndef __SERVERINSTANCE__
#define __SERVERINSTANCE__

#include "ServerType.h"
#include "Vmtype.h"
#include "log.h"
#include <assert.h>
#include <map>
#include <sstream>
#include <string>

using namespace std;

namespace hw {
    // 表示位置的枚举类型
    enum Node{
        A = 0,
        B = 1,
        BOTH = 2,
        UNDEFINED = 3,
    };
    inline const char* NodeName(Node n){
        switch (n) {
            case Node::A:
                return "A";
            case Node::B:
                return "B";
            case Node::BOTH:
                return "BOTH";
            case Node::UNDEFINED:
                return "UNDEFINED";
            default:
                cerr<<"error node value: "<<n<<endl;
                exit(-1);
        }
    }
    struct ServerInstance {
        const ServerType& type;
        int server_id;
        std::map<string, Node> record;
        int a_core_remain, b_core_remain, a_memory_remain, b_memory_remain;

        ServerInstance(const ServerType& servertype, const int &server_id)
                : type(servertype), server_id(server_id) {
            a_core_remain = servertype.core / 2;
            b_core_remain = a_core_remain;
            a_memory_remain = servertype.memory / 2;
            b_memory_remain = a_memory_remain;
        };

        ServerInstance(const ServerType& servertype)
                : type(servertype){
            a_core_remain = servertype.core / 2;
            b_core_remain = a_core_remain;
            a_memory_remain = servertype.memory / 2;
            b_memory_remain = a_memory_remain;
            server_id = -1;
        };
        bool empty() const { return record.empty(); };

        int remain_double_memory() const{
            return min(a_memory_remain, b_memory_remain) * 2;
        }

        int remain_double_core() const {
            return min(a_core_remain, b_core_remain) * 2;
        }

        double remain_ratio() const {
            return 1.0 * (a_core_remain+b_core_remain+a_memory_remain+b_memory_remain) / (type.memory+type.core);
        }

        int remain_memory() const { return b_memory_remain + a_memory_remain; };

        int remain_core() const { return a_core_remain + b_core_remain; };

        double total_usage() const { return 0.4 * remain_memory() + 0.6 * remain_core(); }

        //一些统计信息
        double remain_memory_ratio() const
        /*
         * 返回当前实例的memory剩余比列，[0,1]之间的小数
         */
        {
            return 1.0 * (a_memory_remain + b_memory_remain) / type.memory;
        };

        double remain_core_ratio() const
        /*
        返回当前实例的core剩余比列，[0,1]之间的小数
        */
        {
            return 1.0 * (a_core_remain + b_core_remain) / type.core;
        };

        bool can_fit(const Vmtype &vmtype) const {
            /*
            判断能否把虚拟机放入到物理机Server_Instance中
            如果能放下返回当前物理机节点剩余的情况
            */
            int is_double = vmtype.is_double;
            if (is_double) {
                bool memory_fit = false, core_fit = false;
                if (a_memory_remain >= vmtype.memory / 2 &&
                    b_memory_remain >= vmtype.memory / 2) {
                    memory_fit = true;
                }
                if (a_core_remain >= vmtype.core / 2 &&
                    b_core_remain >= vmtype.core / 2) {
                    core_fit = true;
                }
                return memory_fit && core_fit;
            } else {
                //放在A
                if (a_memory_remain >= vmtype.memory && a_core_remain >= vmtype.core) {
                    return true;
                }
                //放在B
                if (b_memory_remain >= vmtype.memory && b_core_remain >= vmtype.core) {
                    return true;
                }
                return false;
            }
        };

        bool can_fit_a(const Vmtype &vmtype) const {
            assert(!vmtype.is_double);
            return vmtype.memory <= a_memory_remain && vmtype.core <= a_core_remain;
        };

        bool can_fit_b(const Vmtype &vmtype) const {
            assert(!vmtype.is_double);
            return vmtype.memory <= b_memory_remain && vmtype.core <= b_core_remain;
        };

        void try_fit_appointed_node(const Vmtype &vmtype, Node node) {
            bool is_double = vmtype.is_double;
            if (is_double && node == Node::BOTH) {
                if (a_memory_remain < vmtype.memory / 2 ||
                    b_memory_remain < vmtype.memory / 2 ||
                    a_core_remain < vmtype.core / 2 || b_core_remain < vmtype.core / 2) {
                    log("this is unexpected. failed with vm(%s) and instance()", vmtype.repr().c_str());
                    exit(-1);
                }
                a_core_remain -= vmtype.core / 2;
                b_core_remain -= vmtype.core / 2;
                a_memory_remain -= vmtype.memory / 2;
                b_memory_remain -= vmtype.memory / 2;
                node = Node::BOTH;
            } else if (is_double == 0 && node != Node::BOTH) {
                if (node == Node::A && can_fit_a(vmtype)) {
                    a_memory_remain -= vmtype.memory;
                    a_core_remain -= vmtype.core;
                } else if (node == Node::B && can_fit_b(vmtype)) {
                    b_memory_remain -= vmtype.memory;
                    b_core_remain -= vmtype.core;
                } else {
                    /*
                    log("this is unexpected. try_fit() failed with vm(%s) and instance(%s) "
                        "and it a remain core is(%s) a remain memory is(%s),b remain core "
                        "is(%s) b remain memory is(%s) node=(%s)",
                        vmtype.repr().c_str(), self, self.a_core_remain, self.a_memory_remain,
                        self.b_core_remain, self.b_memory_remain, node);
                        */
                    log("Fatal error, but msg is not shown. PLEASE reformat this log info!!!!!!!");
                    exit(-1);
                }
            } else {
                /*
                log("this is unexpected. try_fit() failed with vm(%s) and instance(%s) "
                    "and it a remain core is(%s) a remain memory is(%s),b remain core "
                    "is(%s) b remain memory is(%s) node=(%s)",
                    vmtype, self, self.a_core_remain, self.a_memory_remain,
                    self.b_core_remain, self.b_memory_remain, node);
                    */
                log("Fatal error, but msg is not shown. PLEASE reformat this log info!!!!!!!");
                exit(-1);
            }
        };

        Node try_fit(const Vmtype &vmtype) {
            /*
             * 尝试把虚拟机放在服务器实例中，返回分配到哪个节点，both，A，B
             */
            Node node = Node::UNDEFINED;
            if (!can_fit(vmtype)) {
                return node;
            }
            bool is_double = vmtype.is_double;
            if (is_double) {
                if (a_memory_remain < vmtype.memory / 2 ||
                    b_memory_remain < vmtype.memory / 2 ||
                    a_core_remain < vmtype.core / 2 || b_core_remain < vmtype.core / 2) {
                    /*
                    log("this is unexpected. failed with vm(%s) and instance(%s)", vmtype,
                        self);
                        */
                    log("Fatal error, but msg is not shown. PLEASE reformat this log info!!!!!!!");
                    exit(-1);
                }
                a_core_remain -= vmtype.core / 2;
                b_core_remain -= vmtype.core / 2;
                a_memory_remain -= vmtype.memory / 2;
                b_memory_remain -= vmtype.memory / 2;
                node = Node::BOTH;
            } else {
                // TODO 选择A结点还是B结点
                bool A_first = true;
                if (!A_first) {
                    if (can_fit_a(vmtype) && can_fit_b(vmtype)) {
                        if (a_memory_remain > b_memory_remain)
                            node = Node::A;
                        else
                            node = Node::B;
                    } else if (can_fit_a(vmtype))
                        node = Node::A;
                    else if (can_fit_b(vmtype))
                        node = Node::B;
                } else {
                    if (can_fit_a(vmtype))
                        node = Node::A;
                    else if (can_fit_b(vmtype))
                        node = Node::B;
                }
                assert(node != Node::UNDEFINED);
                if (node == Node::A) {
                    a_memory_remain -= vmtype.memory;
                    a_core_remain -= vmtype.core;
                } else if (node == Node::B) {
                    b_memory_remain -= vmtype.memory;
                    b_core_remain -= vmtype.core;
                } else {
                    fprintf(stderr, "error in try_fit");
                    exit(-1);
                }
            }
            return node;
        };

        Node allocate(const Vmtype &vmtype, const std::string &vmid) {
            /*
            分配虚拟机到当前实例，返回存储到物理的那个结点，‘both，A，B
            如果无法分配，返回NULL
            */
            Node node = try_fit(vmtype);
            if (node == Node::UNDEFINED) {
                log("cannot fit");
                exit(-1);
            }

            record[vmid] = node;
            return node;
        };

        Node allocate_appoint_node(const Vmtype &vmtype, const std::string& vmid,
                                          Node node) {
            /*
            指定把某个虚拟机放在物理机上的某个节点
            */
            try_fit_appointed_node(vmtype, node);
            record[vmid] = node;
            return node;
        };

        void free(const Vmtype &vmtype, const std::string& vmid) {
            /*
            释放虚拟机的资源
            */
            if (record.find(vmid) == record.end()) {
                log("Try to free a vm that is not inside this instance");
                exit(-1);
            }
            if (vmtype.is_double) {
                a_core_remain += vmtype.core / 2;
                b_core_remain += vmtype.core / 2;
                a_memory_remain += vmtype.memory / 2;
                b_memory_remain += vmtype.memory / 2;
            } else {
                if (record[vmid] == Node::A) {
                    a_memory_remain += vmtype.memory;
                    a_core_remain += vmtype.core;
                } else if (record[vmid] == Node::B) {
                    b_memory_remain += vmtype.memory;
                    b_core_remain += vmtype.core;
                } else {
                    log("this is unexpected");
                    exit(-1);
                }
            }
            record.erase(vmid);

        }
    };

}; // namespace hw

#endif